/*
 *  Copyright 2006 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// Most of this was borrowed (with minor modifications) from V8's and Chromium's
// src/base/logging.cc.

#include <cstdarg>
#include <cstdio>
#include <cstdlib>

#if defined(WEBRTC_ANDROID)
#define RTC_LOG_TAG_ANDROID "rtc"
#include <android/log.h>  // NOLINT
#endif

#if defined(WEBRTC_WIN)
#include <windows.h>
#endif

#if defined(WEBRTC_WIN)
#define LAST_SYSTEM_ERROR (::GetLastError())
#elif defined(__native_client__) && __native_client__
#define LAST_SYSTEM_ERROR (0)
#elif defined(WEBRTC_POSIX)
#include <errno.h>
#define LAST_SYSTEM_ERROR (errno)
#endif  // WEBRTC_WIN

#include "rtc_base/checks.h"

namespace rtc {
namespace webrtc_checks_impl {

// Reads one argument from args, appends it to s and advances fmt.
// Returns true iff an argument was sucessfully parsed.
bool ParseArg(va_list* args,
              const CheckArgType** fmt,
              std::ostream& s) {  // no-presubmit-check TODO(webrtc:8982)
  if (**fmt == CheckArgType::kEnd)
    return false;

  switch (**fmt) {
    case CheckArgType::kInt:
      s << va_arg(*args, int);
      break;
    case CheckArgType::kLong:
      s << va_arg(*args, long);
      break;
    case CheckArgType::kLongLong:
      s << va_arg(*args, long long);
      break;
    case CheckArgType::kUInt:
      s << va_arg(*args, unsigned);
      break;
    case CheckArgType::kULong:
      s << va_arg(*args, unsigned long);
      break;
    case CheckArgType::kULongLong:
      s << va_arg(*args, unsigned long long);
      break;
    case CheckArgType::kDouble:
      s << va_arg(*args, double);
      break;
    case CheckArgType::kLongDouble:
      s << va_arg(*args, long double);
      break;
    case CheckArgType::kCharP:
      s << va_arg(*args, const char*);
      break;
    case CheckArgType::kStdString:
      s << *va_arg(*args, const std::string*);
      break;
    case CheckArgType::kVoidP:
      s << reinterpret_cast<std::uintptr_t>(va_arg(*args, const void*));
      break;
    default:
      s << "[Invalid CheckArgType:" << static_cast<int8_t>(**fmt) << "]";
      return false;
  }
  (*fmt)++;
  return true;
}

RTC_NORETURN void FatalLog(const char* file,
                           int line,
                           const char* message,
                           const CheckArgType* fmt,
                           ...) {
  va_list args;
  va_start(args, fmt);

  std::ostringstream ss;  // no-presubmit-check TODO(webrtc:8982)
  ss << "\n\n#\n# Fatal error in: " << file << ", line " << line
     << "\n# last system error: " << LAST_SYSTEM_ERROR << "\n# Check failed: ";

  if (*fmt == CheckArgType::kCheckOp) {
    // This log message was generated by RTC_CHECK_OP, so we have to complete
    // the error message using the operands that have been passed as the first
    // two arguments.
    fmt++;

    std::ostringstream s1, s2;  // no-presubmit-check TODO(webrtc:8982)
    if (ParseArg(&args, &fmt, s1) && ParseArg(&args, &fmt, s2))
      ss << message << " (" << s1.str() << " vs. " << s2.str() << ")\n# ";
  } else {
    ss << message << "\n# ";
  }

  // Append all the user-supplied arguments to the message.
  while (ParseArg(&args, &fmt, ss))
    ;

  va_end(args);

  std::string s = ss.str();
  const char* output = s.c_str();

#if defined(WEBRTC_ANDROID)
  __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", output);
#endif

  fflush(stdout);
  fprintf(stderr, "%s", output);
  fflush(stderr);
  abort();
}

}  // namespace webrtc_checks_impl
}  // namespace rtc

// Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros.
RTC_NORETURN void rtc_FatalMessage(const char* file, int line,
                                   const char* msg) {
  static constexpr rtc::webrtc_checks_impl::CheckArgType t[] = {
      rtc::webrtc_checks_impl::CheckArgType::kEnd};
  FatalLog(file, line, msg, t);
}
